#!usr/bin/env python  
# -*- coding:utf-8 -*-
""" 
@author:robot
@file: title03跳格子.py 
@version:
@time: 2023/05/26

地上共有N个格子，你需要调完地上所有的格子，
但是格子间是由强依赖关系的，跳完前一个格子后，
后续的格子才会被开启，格子间的依赖关系由多组steps数组给出，
step[0]表示前一个格子，steps[1]表示steps[0]可以开启的格子；
比如[0,1]表示从跳完第0个格子后第1个格子就开启了，
比如[2,1],[2,3]表示跳完第2个格子后第1个格子和第3个格子就被开启了
请你计算是否能由给出的steps数组跳完所有的格子，
如果可以输出yes，否则输出no

1. 你可以从一个格子跳到任意一个开启的格子
2. 没有前置依赖条件的格子默认就是开启的
3. 如果总数是N，则所有的给子编号为[0,1,2,3...N-1]连续的数组

输入
3
0 1
0 2

输出
yes

说明
总共有三个格子[0, 1, 2]，跳完0个格子后第1个格子就开启了，跳到第0个格子后第2个格子也被开启了，
按照0->1->2,0->2->1的顺序都可以跳完所有的格子

思路
本题的目标是实现了一个跳格子游戏的逻辑，核心思想是利用栈和字典，按照行列分别存储已经走过的格子，
然后遍历每一个空格格子，判断它是否可以到达边界，最后判断是否所有的空白格子都不能到达边界，从而判断游戏是否可以成功
"""


def solve(n, grids):
    x = [None] * n  # x 为格子
    MAP = {}
    for i in range(len(grids)):
        a = grids[i]  # 取某个step
        if a[0] not in MAP:  # 如果起始位置不在MAP中
            MAP[a[0]] = []  # 设置a[0]=[]
        MAP[a[0]].append(a[1])  # 将a[1]加到a[0]中
        x[a[1]] = a[0]  # 格子结束位置设置为起始位置的值
    stack = []  # 设置栈为空列表
    for i in range(n):  # 轮询n次
        if x[i] is None:  # 如果对应x[i]的值为空，说明没有前置条件
            stack.append(i)  # stack将这个格子加入到stack，说明这个格子是可以作为开始的
    if not stack:  # 如果stack为空，所有格子都需要前置开启，无法开始
        return False  # 那么直接返回False
    while stack:
        index = stack.pop()  # 取出格子位置
        x[index] = -1  # 设置x对应格子为-1
        if index not in MAP:  # 如果格子不作为起始点，那么继续pop
            continue
        for item in MAP[index]:  # 轮询起始节点能到的位置
            if x[item] != -1:  # 如果它不作为其实节点，那么入栈
                stack.append(item)
    return all(val == -1 for val in x)  # 最后看所有元素是否都为-1，否则返回False


def main():
    n = int(input())
    grids = []
    while True:
        _input = input()
        if _input:
            grids.append(list(map(int, _input.split())))
        else:
            break
    res = solve(n, grids)
    print('yes' if res else 'no')


main()
